#include <strings.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <google/protobuf/message.h>
#include <fstream>
//#include <pcap.h>
#include <boost/filesystem.hpp>
#include <netinet/in.h>
#include "util/multi.pb.h"
#include "util/hmac.h"
#include <openssl/hmac.h>
#include <netinet/ip.h>
#include <ctime>

#include "beaconserver.h"
#include "sshclient.h"


#define MULTICASTIP "224.42.42.42"
#define CLIENT_MULTICASTPORT 3200
#define BLOB_SIZE 1000

#define ETHER_TYPE_IP (0x0800)
#define ETHER_TYPE_8021Q (0x8100)

sshQueue sshq;

BlockMap m_beacons;

class Heartbeat_t {
public:
	Heartbeat_t() {
		pthread_mutex_lock(&m_lock);
		m_time = time(0);
		pthread_mutex_unlock(&m_lock);
	}
	void tick() {
		pthread_mutex_lock(&m_lock);
		m_time = time(0);
		pthread_mutex_unlock(&m_lock);
	}
	int check() {
		pthread_mutex_lock(&m_lock);
		time_t tmp = time(0) - m_time;
		pthread_mutex_unlock(&m_lock);
		return tmp;
	}
private:
	time_t m_time;
	pthread_mutex_t m_lock;
};

Heartbeat_t HeartbeatValidator;

class udp_server {
public:
	udp_server(int port) {

		m_sockfd=socket(AF_INET,SOCK_DGRAM,0);

		bzero(&m_servaddr,sizeof(m_servaddr));
		m_servaddr.sin_family = AF_INET;
//		m_servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
		m_servaddr.sin_addr.s_addr=inet_addr("172.16.0.1");
		m_servaddr.sin_port=htons(port);
		bind(m_sockfd,(struct sockaddr *)&m_servaddr,sizeof(m_servaddr));

		m_imreq.imr_multiaddr.s_addr = inet_addr(MULTICASTIP);
		m_imreq.imr_interface.s_addr = inet_addr("172.16.0.1"); //INADDR_ANY; // use DEFAULT interface

		setsockopt(m_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
		           (const void *)&m_imreq, sizeof(struct ip_mreq));

	}
	server::Block getBlock(std::string &ip) 
	{
		while(1)
		{
			socklen_t len;
			char mesg[10000];
			len = sizeof(m_cliaddr);
			int byte_count = recvfrom(m_sockfd,mesg,10000,0,(struct sockaddr *)&m_cliaddr,&len);
			std::string data(mesg,byte_count);

	
			m_cliaddr.sin_addr.s_addr;

			char byte_order[100];
			inet_ntop(AF_INET, &m_cliaddr.sin_addr.s_addr, byte_order, 100);
			std::string ipTmp(byte_order);
			ip = ipTmp;
			
			std::cout << ip << std::endl;

	//		sshq.add(ip, "reboot -d 10");

			if(data.size() >20) {
				std::string hmacStr = data.substr(data.size() - 20,20);
				std::string blockData = data.substr(0,data.size() - 20);

				if(hmacStr == hmac(blockData)) {
					server::Block block;
					block.ParseFromString(blockData);
					return block;
				}
			}
		}
	}
	void rxBlock(server::Block const& block) {
		static int count = 0;

		static int lastSeq=0;
		static int fileID=0;
		static int fileSize=0;

		static std::ofstream myfile;

		if(block.has_heartbeat()) {
			HeartbeatValidator.tick();
		}
		if(block.has_ctrl() && block.ctrl().has_updateserver()) {
			std::cout << "update server" << std::endl;
			if(rename("./download/server", "server"))
				std::cout << "File successfully renamed" << std::endl;
			else
				std::cout << "Error renaming file" << std::endl;
		}

		if(block.has_ctrl() && block.ctrl().has_killprogram()) {
			std::cout << "Kill program" << std::endl;
			std::string command1 = "killall -9 " + block.ctrl().filename();
			system(command1.c_str());
		}
	}
private:
	int m_sockfd,m_n;
	struct ip_mreq m_imreq;
	struct sockaddr_in m_servaddr,m_cliaddr;
};


void *server_system_receiver( void *ptr ) {


}





void* heartbeatReceiverThread(void *arg) {

	udp_server client_process(CLIENT_MULTICASTPORT);



	while(1) {
		std::string ip;
		server::Block block(client_process.getBlock(ip));
		m_beacons[ip] = block;
	
		client_process.rxBlock(block);
	}


	return NULL;
}

beaconserver::beaconserver()
{
		pthread_t heartbeatProcess;
		pthread_create(&(heartbeatProcess), NULL, &heartbeatReceiverThread, NULL);

}


BlockMap beaconserver::getBlockMap()
{
	return m_beacons;
}
